package xin.yangshuai.generator01.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import xin.yangshuai.generator01.entity.ModuleNameAnalysisInfo;
import xin.yangshuai.generator01.service.GeneratorService;
import xin.yangshuai.generator01.service.ModuleService;

import java.io.*;

/**
 * GeneratorServiceImpl
 *
 * @author shuai
 * @date 2024/10/17
 */
@Service
public class GeneratorServiceImpl implements GeneratorService {

    @Value("${absolute-path-prefix}")
    private String absolutePathPrefix;

    @Value("${content-root-path-prefix}")
    private String contentRootPathPrefix;

    @Value("${package-prefix}")
    private String packagePrefix;

    @Autowired
    private ModuleService moduleService;

    private SpringTemplateEngine engine;

    private SpringTemplateEngine getSpringTemplateEngine() {

        if (this.engine == null) {
            SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
            resolver.setApplicationContext(new AnnotationConfigApplicationContext());
            resolver.setPrefix("/codeTemplate/");
            resolver.setSuffix(".template");
            resolver.setCharacterEncoding("UTF-8");
            resolver.setTemplateMode(TemplateMode.TEXT);

            SpringTemplateEngine engine = new SpringTemplateEngine();
            engine.setTemplateResolver(resolver);
            this.engine = engine;
        }
        return engine;
    }

    @Override
    public void create(String moduleName) {
        if (moduleName == null) {
            throw new RuntimeException("moduleName is null");
        }

        if (!moduleName.matches("[a-zA-Z0-9\\-]+")) {
            throw new RuntimeException("moduleName is invalid");
        }

        File moduleFile = new File(absolutePathPrefix + moduleName);
        if (!moduleFile.exists()) {
            throw new RuntimeException(absolutePathPrefix + moduleName + " is not exists");
        }

        if (!moduleFile.isDirectory()) {
            throw new RuntimeException(absolutePathPrefix + moduleName + " is not a directory");
        }

        createApplicationJava(moduleName);
        createApplicationYml(moduleName);
        createControllerJava(moduleName);
        createPom(moduleName);
    }

    private void createApplicationJava(String moduleName) {

        ModuleNameAnalysisInfo moduleNameAnalysisInfo = analysisModuleName(moduleName);
        String version = moduleNameAnalysisInfo.getVersion();
        String relativePath = moduleNameAnalysisInfo.getRelativePath();
        String relativePackage = moduleNameAnalysisInfo.getRelativePackage();
        String upperCamelName = moduleNameAnalysisInfo.getUpperCamelName();

        if (version == null) {
            version = "";
        }

        SpringTemplateEngine engine = getSpringTemplateEngine();

        String packageName = packagePrefix + "." + relativePackage + version;
        String className = upperCamelName + version + "Application";

        Context context = new Context();
        context.setVariable("packageName", packageName);
        context.setVariable("className", className);

        String text = engine.process("application.java", context);

        String directoryPath = absolutePathPrefix + moduleName + "/" + contentRootPathPrefix + relativePath + version;
        File directoryFile = new File(directoryPath);
        if (!directoryFile.exists()) {
            directoryFile.mkdirs();
        }

        try {
            Writer writer = new FileWriter(new File(directoryPath + "/" + className + ".java"));
            writer.write(text);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createApplicationYml(String moduleName) {

        ModuleNameAnalysisInfo moduleNameAnalysisInfo = analysisModuleName(moduleName);
        String version = moduleNameAnalysisInfo.getVersion();
        String hyphenatedName = moduleNameAnalysisInfo.getHyphenatedName();

        if (version == null) {
            version = "";
        }

        SpringTemplateEngine engine = getSpringTemplateEngine();

        String contextPath = hyphenatedName + version;

        // 在所有module中，获取当前的最大端口号
        int maxPort = moduleService.maxPort();
        // 当前模块的端口号 + 10
        int port = maxPort + 10;

        Context context = new Context();
        context.setVariable("port", port);
        context.setVariable("contextPath", contextPath);

        String text = engine.process("application.yml", context);

        String directoryPath = absolutePathPrefix + moduleName + "/src/main/resources";
        File directoryFile = new File(directoryPath);
        if (!directoryFile.exists()) {
            directoryFile.mkdirs();
        }

        try {
            Writer writer = new FileWriter(new File(directoryPath + "/application.yml"));
            writer.write(text);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createControllerJava(String moduleName) {

        ModuleNameAnalysisInfo moduleNameAnalysisInfo = analysisModuleName(moduleName);
        String version = moduleNameAnalysisInfo.getVersion();
        String relativePath = moduleNameAnalysisInfo.getRelativePath();
        String relativePackage = moduleNameAnalysisInfo.getRelativePackage();
        String hyphenatedName = moduleNameAnalysisInfo.getHyphenatedName();

        if (version == null) {
            version = "";
        }

        SpringTemplateEngine engine = getSpringTemplateEngine();

        String packageName = packagePrefix + "." + relativePackage + version + ".controller";

        Context context = new Context();
        context.setVariable("packageName", packageName);
        context.setVariable("moduleShortName", hyphenatedName);

        String text = engine.process("controller.java", context);

        String directoryPath = absolutePathPrefix + moduleName + "/" + contentRootPathPrefix + relativePath + version + "/controller";
        File directoryFile = new File(directoryPath);
        if (!directoryFile.exists()) {
            directoryFile.mkdirs();
        }

        try {
            Writer writer = new FileWriter(new File(directoryPath + "/IndexController.java"));
            writer.write(text);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createPom(String moduleName) {

        String groupId = null;
        String artifactId = null;
        String version = null;

        try (FileInputStream fileInputStream = new FileInputStream(absolutePathPrefix + "pom.xml");) {
            MavenXpp3Reader xpp3Reader = new MavenXpp3Reader();
            Model model = xpp3Reader.read(fileInputStream);
            groupId = model.getGroupId();
            artifactId = model.getArtifactId();
            version = model.getVersion();

            Parent parent = model.getParent();
            if (StringUtils.isBlank(groupId)) {
                if (parent != null) {
                    groupId = parent.getGroupId();
                }
            }
            if (StringUtils.isBlank(version)) {
                if (parent != null) {
                    version = parent.getVersion();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        SpringTemplateEngine engine = getSpringTemplateEngine();

        Context context = new Context();
        context.setVariable("moduleName", moduleName);
        context.setVariable("groupId", groupId);
        context.setVariable("artifactId", artifactId);
        context.setVariable("version", version);

        String text = engine.process("pom", context);

        String directoryPath = absolutePathPrefix + moduleName;
        File directoryFile = new File(directoryPath);
        if (!directoryFile.exists()) {
            directoryFile.mkdirs();
        }

        try {
            Writer writer = new FileWriter(new File(directoryPath + "/pom.xml"));
            writer.write(text);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 分析moduleName
     *
     * @param moduleName
     * @return
     */
    private static ModuleNameAnalysisInfo analysisModuleName(String moduleName) {

        // 对moduleName进行解析，根据-拆分成字符数组，如果数组的最后一个元素可以解析为数字，则认为是版本号，否则认为是名称的一部分

        // 版本号
        String version = null;
        // a/b/c，不包含版本号
        String relativePath = null;
        // a.b.c，不包含版本号
        String relativePackage = null;
        // a-b-c，不含版本号
        String hyphenatedName = null;
        // AbcAbc，不含版本号
        String upperCamelName = null;
        // abcAbc，不含版本号
        String lowerCamelName = null;

        String[] split = moduleName.split("-");
        for (int i = 0; i < split.length; i++) {
            String element = split[i];

            if (i == split.length - 1 && element.matches("\\d+")) {
                version = element;
                break;
            }

            if (relativePath != null) {
                relativePath += "/" + element;
            } else {
                relativePath = element;
            }

            if (relativePackage != null) {
                relativePackage += "." + element;
            } else {
                relativePackage = element;
            }

            if (hyphenatedName != null) {
                hyphenatedName += "-" + element;
            } else {
                hyphenatedName = element;
            }

            String upperCamelElement = element.substring(0, 1).toUpperCase() + element.substring(1);

            if (upperCamelName != null) {
                upperCamelName += upperCamelElement;
            } else {
                upperCamelName = upperCamelElement;
            }
        }


        if (upperCamelName != null) {
            lowerCamelName = upperCamelName.substring(0, 1).toLowerCase() + upperCamelName.substring(1);
        }

        return new ModuleNameAnalysisInfo(version, relativePath, relativePackage, hyphenatedName, upperCamelName, lowerCamelName);
    }
}
